package store.lunangangster.engine.impl.db;

import java.util.HashMap;
import java.util.Map;
import store.lunangangster.engine.impl.entity.DeploymentEntity;
import store.lunangangster.engine.impl.entity.DeploymentEntityImpl;
import store.lunangangster.engine.impl.entity.Entity;
import store.lunangangster.engine.impl.entity.ExecutionEntity;
import store.lunangangster.engine.impl.entity.ExecutionEntityImpl;
import store.lunangangster.engine.impl.entity.HistoryEntity;
import store.lunangangster.engine.impl.entity.HistoryEntityImpl;
import store.lunangangster.engine.impl.entity.IdentityLinkEntity;
import store.lunangangster.engine.impl.entity.IdentityLinkEntityImpl;
import store.lunangangster.engine.impl.entity.MesListenerEntity;
import store.lunangangster.engine.impl.entity.MesListenerEntityImpl;
import store.lunangangster.engine.impl.entity.ProcessDefinitionEntity;
import store.lunangangster.engine.impl.entity.ProcessDefinitionEntityImpl;
import store.lunangangster.engine.impl.entity.ResourceEntity;
import store.lunangangster.engine.impl.entity.ResourceEntityImpl;
import store.lunangangster.engine.impl.entity.TaskEntity;
import store.lunangangster.engine.impl.entity.TaskEntityImpl;
import store.lunangangster.engine.impl.entity.VariableEntity;
import store.lunangangster.engine.impl.entity.VariableEntityImpl;

public class MybatisStatements implements Statements {

  private static final String ENTITY = "Entity";
  private static final String IMPL = "Impl";
  private static final int ENTITY_LENGTH = ENTITY.length();
  private static final int IMPL_LENGTH = IMPL.length();

  protected static final String SELECT = "select";
  protected static final String INSERT = "insert";
  protected static final String BATCH_INSERT = "batchInsert";
  protected static final String UPDATE = "update";
  protected static final String DELETE = "delete";

  // read-only
  protected static final Map<Class<? extends Entity>, String> classToSimpleName = new HashMap<>();

  protected final Map<Class<? extends Entity>, Map<String, String>> statementCache = new HashMap<>();

  static {
    // Entity
    registerClassName(DeploymentEntity.class);
    registerClassName(ProcessDefinitionEntity.class);
    registerClassName(ResourceEntity.class);
    registerClassName(ExecutionEntity.class);
    registerClassName(TaskEntity.class);
    registerClassName(IdentityLinkEntity.class);
    registerClassName(MesListenerEntity.class);
    registerClassName(VariableEntity.class);
    registerClassName(HistoryEntity.class);
    // EntityImpl
    registerClassName(DeploymentEntityImpl.class);
    registerClassName(ProcessDefinitionEntityImpl.class);
    registerClassName(ResourceEntityImpl.class);
    registerClassName(ExecutionEntityImpl.class);
    registerClassName(TaskEntityImpl.class);
    registerClassName(IdentityLinkEntityImpl.class);
    registerClassName(MesListenerEntityImpl.class);
    registerClassName(VariableEntityImpl.class);
    registerClassName(HistoryEntityImpl.class);
  }


  @Override
  public <T extends Class<? extends Entity>> String getSelectStatement(T clazz) {
    return getStatement(clazz, SELECT);
  }

  @Override
  public <T extends Class<? extends Entity>> String getInsertStatement(T clazz) {
    return getStatement(clazz, INSERT);
  }

  @Override
  public <T extends Class<? extends Entity>> String getBatchInsertStatement(T clazz) {
    return getStatement(clazz, BATCH_INSERT);
  }

  @Override
  public <T extends Class<? extends Entity>> String getUpdateStatement(T clazz) {
    return getStatement(clazz, UPDATE);
  }

  @Override
  public <T extends Class<? extends Entity>> String getDeleteStatement(T clazz) {
    return getStatement(clazz, DELETE);
  }

  @Override
  public <T extends Class<? extends Entity>> String getStatement(T clazz, String prefix) {
    String statement = findStatementInCache(clazz, prefix);
    if (statement != null) {
      return statement;
    }
    // className as NameSpace of MybatisMapper.xml
    // statement's id not contains "Entity" or "EntityImpl" suffix
    String entityName = classToSimpleName.get(clazz);
    if (entityName != null) {
      statement = prefix + entityName;
      putStatementToCache(clazz, prefix, statement);
    }

    return statement;
  }

  private static void registerClassName(Class<? extends Entity> clazz) {
    classToSimpleName.put(clazz, removeSuffix(clazz.getSimpleName()));
  }

  private static String removeSuffix(String namespaceWithSuffix) {
    int nextEndIndex = namespaceWithSuffix.length();
    if (namespaceWithSuffix.endsWith(IMPL)) {
      nextEndIndex -= (IMPL_LENGTH + ENTITY_LENGTH);
    }
    if (namespaceWithSuffix.endsWith(ENTITY)) {
      nextEndIndex -= ENTITY_LENGTH;
    }
    return namespaceWithSuffix.substring(0, nextEndIndex);
  }

  private String findStatementInCache(Class<? extends Entity> clazz, String prefix) {
    if (statementCache.containsKey(clazz)) {
      return statementCache.get(clazz).get(prefix);
    }
    return null;
  }

  private void putStatementToCache(Class<? extends Entity> clazz, String prefix, String statement) {
    if (!statementCache.containsKey(clazz)) {
      statementCache.put(clazz, new HashMap<>());
    }
    statementCache.get(clazz).put(prefix, statement);
  }

}
